﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Threading;
using System.Collections;

namespace Spider
{
    public class RequestByAsyn : IRequest
    {
        private static Dictionary<Uri, CookieContainer> DicCookie = new Dictionary<Uri, CookieContainer>();
        public delegate void GetCompleteHandler(PageClass Page);
        public event GetCompleteHandler GetResponseComplete;
        ManualResetEvent allDone = new ManualResetEvent(false);
        public static List<Thread> listThread = new List<Thread>();
        private Encoding encoding;

        public RequestByAsyn()
        {
            this.encoding = Encoding.GetEncoding(DataHandler.Instance.CurrentRule.Charset);
        }

        protected HttpWebRequest GetRequest(Uri Url)
        {
            Uri baseURL = new Uri("http://" + Url.Host);
            CookieContainer cookie = null;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
            request.Timeout = 600000;
            //request.KeepAlive = true;
            request.AllowAutoRedirect = true;
            request.MaximumAutomaticRedirections = 2;
            request.ProtocolVersion = HttpVersion.Version11;
            request.Accept = "text/html";
            request.UserAgent = Spider.Properties.Settings.Default.UserAgent;
            lock (DicCookie)
            {
                if (DicCookie.ContainsKey(baseURL))
                    cookie = DicCookie[baseURL];
                else
                {
                    cookie = new CookieContainer();
                    DicCookie.Add(baseURL, cookie);
                }
                request.CookieContainer = cookie;
            }

            return request;
        }

        public PageClass GetPage(Uri Url)
        {

            if (Url == null)
                return null;

            if (string.IsNullOrEmpty(Url.ToString().Trim()))
                return null;

            Uri baseURL = new Uri("http://" + Url.Host);

            RequestState rstate = new RequestState();

            try
            {
                HttpWebRequest request = GetRequest(Url);
                rstate.Request = request;
                rstate.Url = Url;
                request.BeginGetResponse(new AsyncCallback(this.ResponseCallback), rstate);
                //Thread.Sleep(1000);
                //allDone.WaitOne();
                //rstate.Response.Close();
            }
            catch (WebException ex)
            {
                Log.Instance.WriteException(ErrorMsg(Url.ToString(), ex.Message));
            }
            catch (Exception ex)
            {
                Log.Instance.WriteException(ErrorMsg(Url.ToString(), ex.Message));
            }
            finally
            {
                
            }
            return null;
        }

        private void ResponseCallback(IAsyncResult ar)
        {
            RequestState rs = (RequestState)ar.AsyncState;
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            Stream stream = null;

            try
            {
                if(!listThread.Contains(Thread.CurrentThread))
                  listThread.Add(Thread.CurrentThread);

                request = rs.Request;
                response = (HttpWebResponse)request.EndGetResponse(ar);
                rs.Response = response;
                stream = response.GetResponseStream();
                StringBuilder sb = new StringBuilder();
                int count;
                byte[] buf = new byte[10240];
                Uri baseUri = new Uri("http://" + rs.Url.Host);

                response.Cookies = DicCookie[baseUri].GetCookies(request.RequestUri);

                if (stream == null)
                    return;

                if (response.StatusCode != HttpStatusCode.OK)
                {
                    return;
                }

                if (!response.ContentType.ToLower().StartsWith("text/"))
                {
                    return;
                }

                do
                {
                    count = stream.Read(buf, 0, buf.Length);

                    if (count != 0)
                    {
                        sb.Append(encoding.GetString(buf, 0, count));
                    }
                }
                while (count > 0);

                if (this.GetResponseComplete != null)
                {
                    PageClass page = new PageClass();
                    page.html = sb.ToString();
                    page.URL = rs.Url;
                    this.GetResponseComplete(page);
                }
            }
            catch (WebException ex)
            {
                Log.Instance.WriteException(ErrorMsg(rs.Url.ToString(), ex.Message));
            }
            catch (Exception ex)
            {
                Log.Instance.WriteException(ErrorMsg(rs.Url.ToString(), ex.Message));
            }
            finally
            {
                if (stream != null)
                {
                    //response.Close();
                    stream.Flush();
                    stream.Close();
                    stream = null;
                }
                if (response != null)
                    response.Close();

                //allDone.Set();
            }
        }

        private string ErrorMsg(string URL, string Msg)
        {
            return String.Format("Error Message:{0}    Request URL:{1}  DateTime:{2}", Msg, URL, DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss"));
        }


        #region IDisposable Members

        public void Dispose()
        {
            DicCookie = null;
            listThread = null;
        }

        #endregion
    }

    public class RequestState
    {
        public HttpWebRequest Request;
        public Uri Url;
        public HttpWebResponse Response;

        public RequestState()
        {
            Request = null;
            Url = null;
        }
    }
}
